home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
SGI Developer Toolbox 6.1
/
SGI Developer Toolbox 6.1 - Disc 1.iso
/
toolbox
/
src
/
exampleCode
/
opengl
/
xlib
/
bufferogl.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-11-11
|
20KB
|
554 lines
/*
* Copyright (c) 1993-94, Silicon Graphics, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee, provided
* that the name of Silicon Graphics may not be used in any advertising or
* publicity relating to the software without the specific, prior written
* permission of Silicon Graphics.
*
* THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
* EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
*
* IN NO EVENT SHALL SILICON GRAPHICS BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
* INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER
* RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF THE
* POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* OpenGL(TM) is a trademark of Silicon Graphics, Inc.
*/
/*
* bufferogl: an openGL-Xlib version of Yusuf Attarwala's Motif-based 4.0
* mixed model "bufferglm.c" demo program. This is the "after"
* version, ported from its "before" counterpart, located at
* ../../GLX/buffer/bufferglx.c
*
* bufferogl implements an openGL methodology for creating and
* then switching back-and-forth between single and double
* -buffer visuals or drawing modes.
*
* left mouse : switch to single buffer
* middle mouse: switch to double buffer
* right mouse : animate the green sphere in the current buffer mode
* ESC key : exits the program
*
* the program handles redraw (ConfigureNotify--move and resize) and
* expose (pop, expose, de-iconify) and button (i.e. mouse) events.
* ratmandu -- ported July, 1993
*/
#include <stdio.h>
#include <stdlib.h>
#include <GL/glx.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>
#define top 0 /* parent X window array index */
#define SBglwin 1 /* singlebuffer'd RGBA GL window index */
#define DBglwin 2 /* doublebuffer'd RGBA GL window index */
#define WINMAX 3 /* number of unique windows */
#define Mat1 1 /* we'll use two material definitions, */
#define Mat2 2 /* for lighting, one for each sphere */
static void openwindow(char *);
void Winset(int);
static void resize_buffer(long);
static void clean_exit(void);
void initGL(void);
void setMatrix(void);
void loop(void);
void drawScene(void);
Display *dpy; /* The X server connection */
Atom del_atom; /* for possibility of WM killing app */
Window glwins[WINMAX]; /* array for SB/DB window ids */
GLXContext glcontexts[WINMAX]; /* array for SB/DB window context ids */
float ax, ay, az; /* angles green sphere twirls on */
int xsize, ysize; /* current size-of-window keepers */
long buffermode; /* flag for cur win (single||double) */
XEvent event; /* needs to be global for XIfEvent */
GLUquadricObj *quadObj; /* used to make the two spheres with */
main(argc,argv)
int argc;
char **argv;
{
KeySym keysym; /* for catching */
char buf[4]; /* keyboard events */
int myExpose, myConfigure,
myButtRelease, myKeyPress; /* store which events occur */
myExpose = myConfigure = myButtRelease = myKeyPress = GL_FALSE;
openwindow(argv[0]);
/* initialize *both* windows just as we wud do with winopen/winset */
Winset(DBglwin);
initGL(); /* do GL init stuff */
setMatrix();
/* start out making the singlebuffer window be our current GL window */
Winset(SBglwin);
initGL(); /* do GL init stuff */
setMatrix();
drawScene(); /* and begin by drawing the scene in singlebuffer mode */
/*
* The event loop.
*/
while (1) { /* standard logic: get event(s), process event(s) */
glFlush(); /* For proper DGL performance */
/* this "do while" loop does the `get events' half of the "get events,
* process events" action of the infinite while. this is to ensure
* the event queue is always drained before the events that have come
* in are processed.
*/
do {
XNextEvent(dpy, &event);
switch (event.type) {
/* "Expose" events are sort of like "REDRAW" in gl-speak in
* terms of when a window becomes visible, or a previously
* invisible part becomes visible.
*/
case Expose: /* Exposures */
myExpose = GL_TRUE;
break;
/* "ConfigNotify" events are like "REDRAW" in terms of changes
* to a window's size or position.
*/
case ConfigureNotify: /* Resize GL manually */
if (event.xconfigure.window == glwins[top]) {
/* save the changed width/height of the parent X window */
xsize = event.xconfigure.width;
ysize = event.xconfigure.height;
myConfigure = GL_TRUE;
}
break;
/* Wait for "ButtonRelease" events so the queue doesn't fill up
* the way it wud if the user sits on ButtonPresss.
*/
case ButtonRelease:
if (event.xbutton.button == Button1) { /* LEFTMOUSE: */
buffermode = SBglwin; /* switch to */
myButtRelease = GL_TRUE; /* singlebuffer*/
} else if (event.xbutton.button == Button2) {
buffermode = DBglwin; /* MIDDLEMOUSE: switch */
myButtRelease = GL_TRUE; /* to doublebuffer mode */
} else if (event.xbutton.button == Button3) {
loop(); /* RIGHTMOUSE: */
} /* twirl green sphere */
break;
/* "ClientMessage" is generated if the WM itself is being
* gunned down and sends an exit signal to any running prog.
*/
case ClientMessage:
if (event.xclient.data.l[0] == del_atom)
clean_exit();
break;
/* "KeyPress" events are those that would be generated before
* whenever queueing up any KEYBD key via qdevice.
*/
case KeyPress:
/* save out which unmodified key (i.e. the key was
* not modified w/something like "Shift", "Ctrl",
* or "Alt") got pressed for use below.
*/
XLookupString((XKeyEvent *)&event, buf, 4, &keysym, 0);
myKeyPress = GL_TRUE;
break;
} /* end switch (event.type) */
} while (XPending(dpy)); /* end "do { } while".
* XPending() is like qtest()--it only
* tells you if there're any events
* presently in the queue. it does not
* disturb queue's contents in any way.
*/
/* On an "Expose" event, redraw the affected pop'd or de-iconized window
*/
if (myExpose) {
drawScene(); /* draw the GL stuff */
myExpose = GL_FALSE; /* reset flag--queue now empty */
}
/* On a "ConfigureNotify" event, the GL window has either been moved or
* resized. Respond accordingly and then redraw its contents.
*/
if (myConfigure) {
if (buffermode == SBglwin) {
resize_buffer(DBglwin); /* update viewport & CTM stuff */
resize_buffer(SBglwin); /* for *both* windows and then */
} else if (buffermode == DBglwin) {
resize_buffer(SBglwin);
resize_buffer(DBglwin);
}
drawScene();
myConfigure = GL_FALSE; /* reset flag--queue now empty */
}
/* On a "ButtonRelease" event, the flag gets set only for the LEFTMORE
* or MIDDLEMOUSE. we need to change the buffermode state in this case.
* In either case, XStoreName updates the window's title bar, Winset
* then switches to the current buffermode graphics context, and we
* Map the current window *BEFORE* we unmap the previous one. this
* alleviates a painful "flicker" that occurs if you unmap the previous
* window first.
*/
if (myButtRelease) {
if (buffermode == SBglwin) {
XStoreName(dpy,glwins[top],
"single[LEFTMOUSE]-buffer GL window");
XRaiseWindow(dpy, glwins[SBglwin]);
Winset(SBglwin);
} else if (buffermode == DBglwin) {
XStoreName(dpy,glwins[top],
"double[RIGHTMOUSE]-buffer GL window");
XRaiseWindow(dpy, glwins[DBglwin]);
Winset(DBglwin);
}
myButtRelease = GL_FALSE;
}
/* On a keypress of Esc key, exit program.
*/
if (myKeyPress) {
if (keysym == XK_Escape)
clean_exit();
}
} /* end while(1) */
} /* end main */
static int SBattributeList[] = { GLX_RGBA,
GLX_DEPTH_SIZE, 1,
None };
static int DBattributeList[] = { GLX_RGBA,
GLX_DOUBLEBUFFER,
GLX_DEPTH_SIZE, 1,
None };
/* WaitForNotify:
* used to make sure the MapWindow() calls inside openwindow() occur
* beFORE glXMakeCurrent() is invoked so as to avoid a race condition.
*/
static Bool WaitForNotify(Display *d, XEvent *e, char *arg) {
return (e->type == MapNotify) && (e->xmap.window == (Window)arg);
}
/* openwindow -
* establish connection to X server, get screen info, specify the
* attributes we want the WM to try to provide, and create the 2
* GL windows--one single and one double -buffered--that we'll employ.
*/
static void openwindow(char *progname) {
XSizeHints Winhints; /* used to fix window size */
XSetWindowAttributes SBswa, DBswa;
XColor gray;
XVisualInfo *SBvi, *DBvi;
Colormap SBcmap, DBcmap;
int scrnnum; /* X screen number */
int xorig, yorig;
long scrnheight; /* for defining maximum resize*/
/* Connect to the X server and get screen info */
if ((dpy = XOpenDisplay(NULL)) == NULL) {
fprintf(stderr, "%s: cannot connect to X server %s\n",
progname, XDisplayName(NULL));
exit(1);
}
scrnnum = DefaultScreen(dpy);
scrnheight = DisplayHeight(dpy, scrnnum);
xorig = 0; yorig = 0; /* define window initial size */
xsize = 300; ysize = 300;
/* first create the top level X window which will be the parent to both
* the singlebuffer and doublebuffer rendering windows
*/
glwins[top] = XCreateSimpleWindow(dpy, RootWindow(dpy, scrnnum),
xorig, yorig, xsize, ysize, 0, 0, 0);
if (!(glwins[top])) {
fprintf(stderr,"%s: couldn't create \"parent\" X window\n",progname);
exit(1);
}
/* including this call to XSelectInput shud not be necessary, but there's
* a bug, so we have to resort to this hackaround.
*/
XSelectInput(dpy, glwins[top],
StructureNotifyMask|ButtonPressMask|ButtonReleaseMask|KeyPressMask);
/* define the string that will show up in the window title bar (and icon) */
XStoreName(dpy,glwins[top],"single/double -buffer program");
/* specify the values for the Window Size Hints we want to enforce: this
* window's aspect ratio needs to stay at 1:1, constrain min and max
* window size, and specify the initial size of the window.
*/
Winhints.width = xsize; /* specify desired x/y size of window */
Winhints.height = ysize;
Winhints.min_width = xsize; /* define min and max */
Winhints.max_width = scrnheight-1; /* width and height */
Winhints.min_height = ysize;
Winhints.max_height = scrnheight-1;
Winhints.min_aspect.x = 1; /* keep aspect at a 1:1 ratio */
Winhints.max_aspect.x = 1;
Winhints.min_aspect.y = 1;
Winhints.max_aspect.y = 1;
/* set the corresponding flags */
Winhints.flags = USSize|PMaxSize|PMinSize|PAspect;
XSetNormalHints(dpy, glwins[top], &Winhints);
/* now define and create the single and doublebuffer rendering windows */
/* first get the appropriate visuals */
SBvi = glXChooseVisual(dpy, scrnnum, SBattributeList);
DBvi = glXChooseVisual(dpy, scrnnum, DBattributeList);
/* create the appropriate GLX contexts */
glcontexts[SBglwin] = glXCreateContext(dpy, SBvi, 0, GL_TRUE);
glcontexts[DBglwin] = glXCreateContext(dpy, DBvi, 0, GL_TRUE);
/* now create the two colormaps */
SBcmap = XCreateColormap(dpy, RootWindow(dpy, SBvi->screen),
SBvi->visual, AllocNone);
DBcmap = XCreateColormap(dpy, RootWindow(dpy, DBvi->screen),
DBvi->visual, AllocNone);
/* now create both windows */
SBswa.colormap = SBcmap;
SBswa.border_pixel = 0;
SBswa.event_mask = StructureNotifyMask | ButtonReleaseMask |
ButtonPressMask | KeyPressMask; /* express interest in events */
glwins[SBglwin] = XCreateWindow(dpy, glwins[top],
xorig, yorig, xsize, ysize,
0, SBvi->depth, InputOutput, SBvi->visual,
CWBorderPixel|CWColormap|CWEventMask, &SBswa);
DBswa.colormap = DBcmap;
DBswa.border_pixel = 0;
DBswa.event_mask = StructureNotifyMask | ButtonReleaseMask |
ButtonPressMask | KeyPressMask; /* express interest in events */
glwins[DBglwin] = XCreateWindow(dpy, glwins[top],
xorig, yorig, xsize, ysize,
0, DBvi->depth, InputOutput, DBvi->visual,
CWBorderPixel|CWColormap|CWEventMask, &DBswa);
/* now map both windows, but leave the singlebuffer window on top */
XMapWindow(dpy, glwins[SBglwin]);
XMapWindow(dpy, glwins[DBglwin]);
XIfEvent(dpy, &event, WaitForNotify, (char*)glwins[DBglwin]);
XRaiseWindow(dpy, glwins[SBglwin]);
XIfEvent(dpy, &event, WaitForNotify, (char*)glwins[SBglwin]);
XMapWindow(dpy, glwins[top]);
/* connect the context to the window */
glXMakeCurrent(dpy, glwins[SBglwin], glcontexts[SBglwin]);
/* ensure the GL colormap is installed for this app */
XSetWMColormapWindows(dpy, glwins[top], glwins, WINMAX);
/* express interest in WM being able to kill this app */
if ((del_atom = XInternAtom(dpy, "WM_DELETE_WINDOW", True)) != None)
XSetWMProtocols(dpy, glwins[top], &del_atom, 1);
glFlush();
}
char *typeToName[] = {
"parent window",
"RGB single buffer",
"RGB double buffer",
};
/* A little helper wrapper for glXMakeCurrent.
* passes the index to jointly access the windows and contexts array and
* checks the return value. This makes the call to begin GL drawing a
* little simpler. Building in such automatic error checking is always a
* "smooth move" (*not* like the cancerously-mutant human with the enlarged
* proboscis plastered all over the place urging people to be likewise
* smoothly cancerous and hardly cool).
*/
void Winset(int type)
{
int rv;
/*
XIfEvent(dpy, &event, WaitForNotify, (char*)glwins[type]);
*/
XSync(dpy,GL_FALSE);
rv = glXMakeCurrent(dpy, glwins[type], glcontexts[type]);
if (rv == GL_FALSE) {
fprintf(stderr, "glXMakeCurrent failed for an %s-type window\n",
typeToName[type]);
exit(-1);
}
}
/* window has been moved or resized so update viewport & CTM stuff.
*/
static void resize_buffer(long wintype) {
Winset(wintype);
XMoveResizeWindow(dpy, glwins[wintype], 0, 0, xsize, ysize);
glViewport(0, 0, xsize-1, ysize-1);
}
/* clean up before exiting
*/
static void clean_exit(void)
{
XCloseDisplay(dpy);
exit(0);
}
/* setup all necessary GL initialzation parameters.
* do our materials/lighting definitions, enable what we need, and define
* the properites with gluQuadric for our two sphere objects.
*/
void
initGL()
{
GLfloat mat1_diff[] = { 0.6, 0.2, 0.2, 1.0 };
GLfloat mat1_spec[] = { 1.0, 1.0, 1.0, 1.0 };
GLfloat mat1_shin[] = { 120.0 };
GLfloat mat2_diff[] = { 0.2, 0.8, 0.2, 1.0 };
GLfloat mat2_spec[] = { 1.0, 1.0, 1.0, 1.0 };
GLfloat mat2_shin[] = { 120.0 };
GLfloat light_ambi[] = { 0.2, 0.2, 0.2, 1.0 };
GLfloat light_diff[] = { 0.9, 0.9, 0.9, 1.0 }; /* diffuse & specular */
GLfloat light_spec[] = { 0.9, 0.9, 0.9, 1.0 }; /* are now equivalent */
GLfloat light_posi[] = { 10.0, 50.0, 50.0, 0.0 }; /* to the old LCOLOR */
GLfloat liteModel_ambient[] = { 0.3, 0.3, 0.3, 1.0 };
glNewList(Mat1, GL_COMPILE);
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat1_diff);
glMaterialfv(GL_FRONT, GL_SPECULAR, mat1_spec);
glMaterialfv(GL_FRONT, GL_SHININESS, mat1_shin);
glEndList();
glNewList(Mat2, GL_COMPILE);
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat2_diff);
glMaterialfv(GL_FRONT, GL_SPECULAR, mat2_spec);
glMaterialfv(GL_FRONT, GL_SHININESS, mat2_shin);
glEndList();
glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambi);
glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diff);
glLightfv(GL_LIGHT0, GL_SPECULAR, light_spec);
glLightfv(GL_LIGHT0, GL_POSITION, light_posi);
glLightModelfv (GL_LIGHT_MODEL_AMBIENT, liteModel_ambient);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_DEPTH_TEST);
quadObj = gluNewQuadric(); /* used to define the two spheres */
gluQuadricDrawStyle(quadObj,GLU_FILL); /* draw the spheres as polygons */
gluQuadricNormals(quadObj, GLU_SMOOTH); /* make the spheres smooth */
gluQuadricOrientation(quadObj,GLU_OUTSIDE); /* draw w/nrmls pnting out */
buffermode = SBglwin; /* start out in singlebuffer mode */
}
/* define our Current Transformation Matrix
*/
void
setMatrix()
{
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-10.0, 10.0, -10.0*ysize/xsize, 10.0*ysize/xsize, -10.0, 10.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(0.0, 0.0, 4.0, 0.0, -1.5, 0.0, 0.0, 1.0, 0.0);
}
/* draw our two spheres
*/
void
drawScene()
{
glClearColor(0.3, 0.3, 0.3, 0.0);
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glPushMatrix();
glPushMatrix();
glCallList(Mat1);
glTranslatef(0.2, 0.2, 0.2);
gluSphere(quadObj, 3.0, 20, 20);
glPopMatrix();
glRotatef(-ax, 1.0, 0.0, 0.0);
glRotatef(ay, 0.0, 1.0, 0.0);
glRotatef(az, 0.0, 0.0, 1.0);
glTranslatef(3.4, 3.4, 0.2);
glCallList(Mat2);
gluSphere(quadObj, 4.0, 20, 20);
glPopMatrix();
if (buffermode == DBglwin)
glXSwapBuffers(dpy, glwins[DBglwin]);
glFlush();
}
/* perform one loop of twirling the green sphere
*/
void
loop()
{
register int i;
for (i=0;i<50;i++) {
ax += 5.0;
ay -= 2.0;
az += 5.0;
if (ax >= 360.0) ax = 0.0;
if (ay <= -360.0) ay = 0.0;
if (az >= 360.0) az = 0.0;
drawScene();
}
}